home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Games / Strategy / Puzzle / GameMaster / GM Dev Kit / Rulebook Sources / Chess / ChessMoves.p < prev    next >
Encoding:
Text File  |  1991-12-05  |  12.7 KB  |  484 lines  |  [TEXT/PJMM]

  1. unit ChessMoves;
  2.  
  3. { ©1991 Quinn "The Eskimo" }
  4.  
  5. interface
  6.  
  7.     uses
  8.         ChessTypes, Debugs, Failure, {}
  9.         ChessSubs, ChessBoardSubs;
  10.  
  11.     function CheckCheck (var board: boardType; at: boardCoord): boolean;
  12.     procedure CalculateValidSet (var board: boardType; var state: chessState; from: boardCoord; {}
  13.                                     var valid: boardSet; var valid_count: integer);
  14.     function NoValidMoves (player: playerType; var state: chessState; var board: boardType): boolean;
  15.  
  16.     procedure InitState (var state: chessState);
  17.     procedure UpdateState (var state: chessState; var board: boardType; from: boardCoord; toc: boardCoord);
  18.  
  19. implementation
  20.  
  21.     const
  22.         kExBoardXMax = kBoardXMax + 2;
  23.         kExBoardYMax = kBoardYMax + 2;
  24.     type
  25.         exBoardXNdx = -2..kExBoardXMax;
  26.         exBoardYNdx = -2..kExBoardYMax;
  27.  
  28.     function OnBoard (x: exBoardXNdx; y: exBoardYNdx): boolean;
  29.     begin
  30.         OnBoard := (x >= 0) and (x <= kBoardXMax) and (y >= 0) and (y <= kBoardYMax);
  31.     end; { OnBoard }
  32.  
  33.     function CheckCheck (var board: boardType; at: boardCoord): boolean;
  34.  
  35.         function Scan (dx, dy: integer; dangerous: pieceSet): boolean;
  36.             var
  37.                 nx: exBoardXNdx;
  38.                 ny: exBoardYNdx;
  39.                 leave: boolean;
  40.         begin
  41.             Scan := false;
  42.             nx := at.x + dx;
  43.             ny := at.y + dy;
  44.             leave := false;
  45.             while OnBoard(nx, ny) and not leave do begin
  46.                 if board[nx, ny].occupant = Oempty then begin
  47.                     nx := nx + dx;
  48.                     ny := ny + dy;
  49.                 end
  50.                 else if board[nx, ny].occupant in dangerous then begin
  51.                     leave := true;
  52.                     Scan := true;
  53.                 end
  54.                 else begin
  55.                     leave := true;
  56.                 end; { if }
  57.             end; { while }
  58.         end; { Scan }
  59.  
  60.         function Test (x: exBoardXNdx; y: exBoardYNdx; piece: pieceType): boolean;
  61.         begin
  62.             Test := false;
  63.             if OnBoard(x, y) then begin
  64.                 if board[x, y].occupant = piece then begin
  65.                     Test := true;
  66.                 end; { if }
  67.             end; { if }
  68.         end; { Test }
  69.  
  70.         function DoIt (queen, king, bishop, knight, rook, pawn: pieceType; dy: integer): boolean;
  71.             var
  72.                 incheck: boolean;
  73.                 diags, squares: pieceSet;
  74.         begin
  75.             incheck := false;
  76.             if not incheck then
  77.                 incheck := Test(at.x + 1, at.y + dy, pawn);
  78.             if not incheck then
  79.                 incheck := Test(at.x - 1, at.y + dy, pawn);
  80.  
  81.             if not incheck then
  82.                 incheck := Test(at.x + 1, at.y + 2, knight);
  83.             if not incheck then
  84.                 incheck := Test(at.x + 1, at.y - 2, knight);
  85.             if not incheck then
  86.                 incheck := Test(at.x + 2, at.y + 1, knight);
  87.             if not incheck then
  88.                 incheck := Test(at.x + 2, at.y - 1, knight);
  89.             if not incheck then
  90.                 incheck := Test(at.x - 1, at.y + 2, knight);
  91.             if not incheck then
  92.                 incheck := Test(at.x - 1, at.y - 2, knight);
  93.             if not incheck then
  94.                 incheck := Test(at.x - 2, at.y + 1, knight);
  95.             if not incheck then
  96.                 incheck := Test(at.x - 2, at.y - 1, knight);
  97.  
  98.             if not incheck then
  99.                 incheck := Test(at.x + 1, at.y + 1, king);
  100.             if not incheck then
  101.                 incheck := Test(at.x + 1, at.y, king);
  102.             if not incheck then
  103.                 incheck := Test(at.x + 1, at.y - 1, king);
  104.             if not incheck then
  105.                 incheck := Test(at.x, at.y + 1, king);
  106.             if not incheck then
  107.                 incheck := Test(at.x, at.y - 1, king);
  108.             if not incheck then
  109.                 incheck := Test(at.x - 1, at.y + 1, king);
  110.             if not incheck then
  111.                 incheck := Test(at.x - 1, at.y, king);
  112.             if not incheck then
  113.                 incheck := Test(at.x - 1, at.y - 1, king);
  114.  
  115.             if not incheck then begin
  116.                 diags := [queen, bishop];
  117.                 squares := [queen, rook];
  118.                 incheck := Scan(1, 0, squares);
  119.                 if not incheck then
  120.                     incheck := Scan(-1, 0, squares);
  121.                 if not incheck then
  122.                     incheck := Scan(0, 1, squares);
  123.                 if not incheck then
  124.                     incheck := Scan(0, -1, squares);
  125.                 if not incheck then
  126.                     incheck := Scan(1, 1, diags);
  127.                 if not incheck then
  128.                     incheck := Scan(-1, 1, diags);
  129.                 if not incheck then
  130.                     incheck := Scan(1, -1, diags);
  131.                 if not incheck then
  132.                     incheck := Scan(-1, -1, diags);
  133.             end; { if }
  134.  
  135.             DoIt := incheck;
  136.         end; { DoIt }
  137.  
  138.     begin
  139.         if board[at.x, at.y].occupant = OkingW then begin
  140.             CheckCheck := DoIt(OqueenB, OkingB, ObishopB, OknightB, OrookB, OpawnB, -1);
  141.         end
  142.         else if board[at.x, at.y].occupant = OkingB then begin
  143.             CheckCheck := Doit(OqueenW, OkingW, ObishopW, OknightW, OrookW, OpawnW, 1);
  144.         end
  145.         else begin
  146.             Failure('CheckCheck on not king');
  147.         end; { if }
  148.     end; { CheckCheck }
  149.  
  150.     procedure CalculateValidSet (var board: boardType; var state: chessState; from: boardCoord; {}
  151.                                     var valid: boardSet; var valid_count: integer);
  152.         var
  153.             occ: pieceType;
  154.             king: boardCoord;
  155.             myking: pieceType;
  156.             player: playerType;
  157.  
  158.         function CellEmpty (x: boardXNdx; y: boardYNdx): boolean;
  159.         begin
  160.             CellEmpty := board[x, y].occupant = Oempty;
  161.         end; { CellEmpty }
  162.  
  163.         function CellOpposed (x: boardXNdx; y: boardYNdx): boolean;
  164.         begin
  165.             CellOpposed := PieceInMyTeam(Opposite(player), board[x, y].occupant);
  166.         end; { CellOpposed }
  167.  
  168.         function CanMove (x: exBoardXNdx; y: exBoardXNdx): boolean;
  169.         begin
  170.             CanMove := false;
  171.             if OnBoard(x, y) then begin
  172.                 CanMove := CellEmpty(x, y) or CellOpposed(x, y);
  173.             end; { if }
  174.         end; { CanMove }
  175.  
  176.         procedure AddCell (x: boardXNdx; y: boardXNdx);
  177.             var
  178.                 oldsrc, olddest: pieceType;
  179.         begin
  180.             oldsrc := board[from.x, from.y].occupant;
  181.             olddest := board[x, y].occupant;
  182.             if oldsrc = myking then begin
  183.                 king.x := x;
  184.                 king.y := y;
  185.             end; { if }
  186.             board[from.x, from.y].occupant := Oempty;
  187.             board[x, y].occupant := oldsrc;
  188.             if not CheckCheck(board, king) then begin
  189.                 valid[x, y] := true;
  190.                 valid_count := valid_count + 1;
  191.             end; { if }
  192.             board[from.x, from.y].occupant := oldsrc;
  193.             if oldsrc = myking then begin
  194.                 king := from;
  195.             end; { if }
  196.             board[x, y].occupant := olddest;
  197.         end; { AddCell }
  198.  
  199.         procedure AddIfCanMove (x: exBoardXNdx; y: exBoardXNdx);
  200.         begin
  201.             if CanMove(x, y) then begin
  202.                 AddCell(x, y);
  203.             end; { if }
  204.         end; { AddIfCanMove }
  205.  
  206.         procedure AddIfCanTake (x: exBoardXNdx; y: exBoardYNdx);
  207.         begin
  208.             if OnBoard(x, y) then begin
  209.                 if CellOpposed(x, y) then begin
  210.                     AddCell(x, y);
  211.                 end; { if }
  212.             end; { if }
  213.         end; { AddifCanTake }
  214.  
  215.         procedure AddIfCellEmpty (x: exBoardXNdx; y: exBoardYNdx);
  216.         begin
  217.             if OnBoard(x, y) then begin
  218.                 if CellEmpty(x, y) then begin
  219.                     AddCell(x, y);
  220.                 end; { if }
  221.             end; { if }
  222.         end; { AddIfCellEmpty }
  223.  
  224.         procedure AddLine (dx, dy: integer);
  225.             var
  226.                 nx: exBoardXNdx;
  227.                 ny: exBoardYNdx;
  228.                 leave: boolean;
  229.         begin
  230.             nx := from.x + dx;
  231.             ny := from.y + dy;
  232.             leave := false;
  233.             while OnBoard(nx, ny) and not leave do begin
  234.                 if CellEmpty(nx, ny) then begin
  235.                     AddCell(nx, ny);
  236.                     nx := nx + dx;
  237.                     ny := ny + dy;
  238.                 end
  239.                 else if CellOpposed(nx, ny) then begin
  240.                     AddCell(nx, ny);
  241.                     leave := true;
  242.                 end
  243.                 else begin
  244.                     leave := true; { Cell occupied by friendly forces }
  245.                 end; { if }
  246.             end; { while }
  247.         end; { AddLine }
  248.  
  249.         procedure DoPawn;
  250.             var
  251.                 dy, dx: integer;
  252.                 base_row: boardYNdx;
  253.         begin
  254.             base_row := PawnBaseRow(player);
  255.             dy := Pawn_dy(player);
  256.             AddIfCanTake(from.x - 1, from.y + dy);
  257.             AddIfCanTake(from.x + 1, from.y + dy);
  258.             if OnBoard(from.x, from.y + dy) then begin
  259.                 if CellEmpty(from.x, from.y + dy) then begin
  260.                     AddCell(from.x, from.y + dy);
  261.                     if from.y = base_row then begin
  262.                         AddIfCellEmpty(from.x, from.y + 2 * dy);
  263.                     end; { if }
  264.                 end; { if }
  265.             end; { if }
  266. { Handle the case where pawns can take other pawns that skipped passed them using the }
  267. { special case two moves in one from the start position }
  268. { This has a wonderful name that I dont know!  [AJW] seems to think its called en-passent }
  269.             with state[Opposite(player)] do begin
  270.                 if canevilpawn then begin
  271.                     if (evilpawn.y = from.y) and (abs(evilpawn.x - from.x) <= 1) then begin
  272.                         if evilpawn.x < from.x then begin
  273.                             dx := -1;
  274.                         end
  275.                         else begin
  276.                             dx := 1;
  277.                         end; { if }
  278.                         AddCell(from.x + dx, from.y + dy);
  279.                     end; { if }
  280.                 end; { if }
  281.             end; { with }
  282.         end; { DoPawn }
  283.  
  284.         procedure DoRook;
  285.         begin
  286.             AddLine(-1, 0);
  287.             AddLine(0, -1);
  288.             AddLine(1, 0);
  289.             AddLine(0, 1);
  290.         end; { DoRook }
  291.  
  292.         procedure DoKnight;
  293.         begin
  294.             AddIfCanMove(from.x + 1, from.y + 2);
  295.             AddIfCanMove(from.x + 1, from.y - 2);
  296.             AddIfCanMove(from.x + 2, from.y + 1);
  297.             AddIfCanMove(from.x + 2, from.y - 1);
  298.             AddIfCanMove(from.x - 1, from.y + 2);
  299.             AddIfCanMove(from.x - 1, from.y - 2);
  300.             AddIfCanMove(from.x - 2, from.y + 1);
  301.             AddIfCanMove(from.x - 2, from.y - 1);
  302.         end; { DoKnight }
  303.  
  304.         procedure DoBishop;
  305.         begin
  306.             AddLine(1, 1);
  307.             AddLine(-1, 1);
  308.             AddLine(1, -1);
  309.             AddLine(-1, -1);
  310.         end; { DoBishop }
  311.  
  312.         procedure DoKing;
  313.  
  314.             function ThroughCheck (which: boardCoord): boolean;
  315.             begin
  316. { Swap the king and the whichx,whichy position to test whether the king is moving through check}
  317. { I dont want to use the code in AddCell because of its evil state changes }
  318. { We know that which,whichy starts empty because of the check above }
  319.                 board[which.x, which.y].occupant := board[king.x, king.y].occupant;
  320.                 board[king.x, king.y].occupant := Oempty;
  321.                 ThroughCheck := CheckCheck(board, which);
  322.                 board[king.x, king.y].occupant := board[which.x, which.y].occupant;
  323.                 board[which.x, which.y].occupant := Oempty;
  324.             end; { ThroughCheck }
  325.  
  326.             var
  327.                 test: boardCoord;
  328.         begin
  329.             AddifCanMove(from.x + 1, from.y + 1);
  330.             AddifCanMove(from.x + 1, from.y);
  331.             AddifCanMove(from.x + 1, from.y - 1);
  332.             AddifCanMove(from.x, from.y + 1);
  333.             AddifCanMove(from.x, from.y - 1);
  334.             AddifCanMove(from.x - 1, from.y + 1);
  335.             AddifCanMove(from.x - 1, from.y);
  336.             AddifCanMove(from.x - 1, from.y - 1);
  337.             test.y := BaseRow(player);
  338.             if state[player].cancastleleft then begin
  339.                 if CellEmpty(1, test.y) and CellEmpty(2, test.y) and CellEmpty(3, test.y) then begin
  340.                     if not CheckCheck(board, king) then begin
  341.                         test.x := 3;
  342.                         if not ThroughCheck(test) then begin
  343.                             AddCell(2, test.y);
  344.                         end; { if }
  345.                     end; { if }
  346.                 end; { if }
  347.             end; { if }
  348.             if state[player].cancastleright then begin
  349.                 if CellEmpty(5, test.y) and CellEmpty(6, test.y) then begin
  350.                     if not CheckCheck(board, king) then begin
  351.                         test.x := 5;
  352.                         if not ThroughCheck(test) then begin
  353.                             AddCell(6, test.y);
  354.                         end; { if }
  355.                     end; { if }
  356.                 end; { if }
  357.             end; { if }
  358.         end; { DoKing }
  359.  
  360.         procedure DoQueen;
  361.         begin
  362.             AddLine(1, 0);
  363.             AddLine(-1, 0);
  364.             AddLine(0, 1);
  365.             AddLine(0, -1);
  366.             AddLine(1, 1);
  367.             AddLine(-1, 1);
  368.             AddLine(1, -1);
  369.             AddLine(-1, -1);
  370.         end; { DoQueen }
  371.  
  372.     begin
  373.         occ := board[from.x, from.y].occupant;
  374.         player := PieceToPlayer(occ);
  375.         myking := PlayerToKing(player);
  376.         ClearBoardSet(valid);
  377.         if not FindPiece(myking, board, king) then begin
  378.             Failure('King not found');
  379.         end; { if }
  380.         valid_count := 0;
  381.         case occ of
  382.             OpawnB, OpawnW: 
  383.                 DoPawn;
  384.             OrookB, OrookW: 
  385.                 DoRook;
  386.             OknightB, OknightW: 
  387.                 DoKnight;
  388.             ObishopB, ObishopW: 
  389.                 DoBishop;
  390.             OkingB, OkingW: 
  391.                 DoKing;
  392.             OqueenB, OqueenW: 
  393.                 DoQueen;
  394.             otherwise
  395.                 Failure('case error');
  396.         end; { case }
  397.     end; { CalculateValidSet }
  398.  
  399.     function NoValidMoves (player: playerType; var state: chessState; var board: boardType): boolean;
  400.         var
  401.             x: boardXNdx;
  402.             y: boardYNdx;
  403.             pos: boardCoord;
  404.             valid: boardSet;
  405.             count: integer;
  406.     begin
  407.         for x := 0 to kBoardXMax do begin
  408.             for y := 0 to kBoardYMax do begin
  409.                 if PieceInMyTeam(player, board[x, y].occupant) then begin
  410.                     pos.x := x;
  411.                     pos.y := y;
  412.                     CalculateValidSet(board, state, pos, valid, count);
  413.                     if count > 0 then begin
  414.                         NoValidMoves := false;
  415.                         exit(NoValidMoves);
  416.                     end; { if }
  417.                 end; { if }
  418.             end; { for }
  419.         end; { for }
  420.         NoValidMoves := true;
  421.     end; { NoValidMoves }
  422.  
  423.     procedure InitState (var state: chessState);
  424.         procedure InitOne (p: playerType);
  425.         begin
  426.             state[p].cancastleleft := true;
  427.             state[p].cancastleright := true;
  428.             state[p].canevilpawn := false;
  429.             state[p].evilpawn.x := 0;
  430.             state[p].evilpawn.y := 0;
  431.         end; { InitOne }
  432.     begin
  433.         InitOne(Pblack);
  434.         InitOne(Pwhite);
  435.     end; { InitState }
  436.  
  437.     procedure UpdateState (var state: chessState; var board: boardType; from: boardCoord; toc: boardCoord);
  438.         var
  439.             occ: pieceType;
  440.             player: playerType;
  441.  
  442.         procedure UpdateForPawn;
  443.         begin
  444.             if abs(from.y - toc.y) > 1 then begin
  445.                 state[player].canevilpawn := true;
  446.                 state[player].evilpawn := toc;
  447.             end; { if }
  448.         end; { UpdateForPawn }
  449.  
  450.         procedure UpdateForRook;
  451.             var
  452.                 y: boardYNdx;
  453.         begin
  454.             y := BaseRow(player);
  455.             if from.x = 0 then begin
  456.                 if from.y = y then begin
  457.                     state[player].cancastleleft := false;
  458.                 end; { if }
  459.             end; { if }
  460.             if from.x = kBoardXMax then begin
  461.                 if from.y = y then begin
  462.                     state[player].cancastleright := false;
  463.                 end; { if }
  464.             end; { if }
  465.         end; { UpdateForRook }
  466.  
  467.     begin
  468.         state[Pblack].canevilpawn := false;
  469.         state[Pwhite].canevilpawn := false;
  470.         occ := board[from.x, from.y].occupant;
  471.         player := PieceToPlayer(occ);
  472.         case occ of
  473.             OpawnB, OpawnW: 
  474.                 UpdateForPawn;
  475.             OrookB, OrookW: 
  476.                 UpdateForRook;
  477.             OkingB, OkingW:  begin
  478.                 state[player].cancastleleft := false;
  479.                 state[player].cancastleright := false;
  480.             end;
  481.         end; { case }
  482.     end; { UpdateState }
  483.  
  484. end. { ChessMoves }